/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.engine.cmd.application;

import cn.easyplatform.contexts.ListContext;
import cn.easyplatform.contexts.RecordContext;
import cn.easyplatform.dos.FieldDo;
import cn.easyplatform.engine.runtime.datalist.DataListUtils;
import cn.easyplatform.interceptor.AbstractCommand;
import cn.easyplatform.interceptor.CommandContext;
import cn.easyplatform.lang.Lang;
import cn.easyplatform.messages.request.ExpressionRequestMessage;
import cn.easyplatform.messages.response.SimpleResponseMessage;
import cn.easyplatform.messages.vos.ExpressionVo;
import cn.easyplatform.messages.vos.datalist.ListExpressionVo;
import cn.easyplatform.support.scripting.ScriptEngineFactory;
import cn.easyplatform.support.sql.SqlParser;
import cn.easyplatform.type.Constants;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.util.MessageUtils;
import cn.easyplatform.util.RuntimeUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class ExecuteExpressionCmd extends
        AbstractCommand<ExpressionRequestMessage> {

    /**
     * @param req
     */
    public ExecuteExpressionCmd(ExpressionRequestMessage req) {
        super(req);
    }

    @Override
    public IResponseMessage<?> execute(CommandContext cc) {
        ExpressionVo lv = req.getBody();
        RecordContext rc = null;
        if (lv instanceof ListExpressionVo) {
            ListExpressionVo llv = (ListExpressionVo) req.getBody();
            ListContext lc = cc.getWorkflowContext().getList(llv.getId());
            if (lc == null)
                return MessageUtils.dataListNotFound(llv.getId());
            rc = lc.getRecord(llv.getKeys());
            if (rc == null) {
                if (lc.getType().equals(Constants.DETAIL))
                    rc = lc.getRecord(llv.getKeys());
                else if (lc.isCustom())
                    rc = lc.createRecord(llv.getKeys());
                else
                    rc = lc.createRecord(llv.getKeys(),
                            DataListUtils.getRecord(cc, lc, llv.getKeys()));
                if (rc == null)
                    return MessageUtils.recordNotFound(lc.getBean().getTable(),
                            Lang.concat(llv.getKeys()).toString());
            }
        } else
            rc = cc.getWorkflowContext().getRecord();
        if (req.getBody().getValues() != null) {
            for (Map.Entry<String, Object> entry : req.getBody().getValues()
                    .entrySet())
                rc.setValue(entry.getKey(), entry.getValue());
        }
        switch (req.getBody().getType()) {
            case 1:
                return executeLogic(cc, rc, req.getBody().getExpr());
            case 2:
                return selectObject(cc, rc, req.getBody().getExpr());
            case 3:
                return selectList(cc, rc, req.getBody().getExpr());
            case 4:
                return executeUpdate(cc, rc, req.getBody().getExpr());
            case 5:
                return executeExpression(cc, rc, req.getBody().getExpr());
            default:
                return selectMap(cc, rc, req.getBody().getExpr());
        }
    }

    private IResponseMessage<?> executeExpression(CommandContext cc,
                                                  RecordContext rc, String expr) {
        Object obj = ScriptEngineFactory.createExpressionEngine().eval(cc,
                expr, rc);
        return new SimpleResponseMessage(obj);
    }

    private IResponseMessage<?> executeLogic(CommandContext cc,
                                             RecordContext rc, String expr) {
        int actionFlag = req.getActionFlag();
        Object obj = null;
        if (cc.getBreakPoint() != null) {
            if (actionFlag < 0) {
                cc.setBreakPoint(null);
                return new SimpleResponseMessage();
            } else
                obj = RuntimeUtils.evaluate(cc, cc.getBreakPoint());
        } else
            obj = RuntimeUtils.evaluate(cc, expr, rc);
        if (obj != null && obj instanceof String
                && obj.toString().length() == 4) {
            String code = (String) obj;
            if ("0000".equals(code))
                return new SimpleResponseMessage();
            return new SimpleResponseMessage(code, cc.getMessage(
                    code, rc));
        }
        return new SimpleResponseMessage(obj);
    }

    private IResponseMessage<?> selectObject(CommandContext cc,
                                             RecordContext rc, String expr) {
        SqlParser<FieldDo> sp = RuntimeUtils.createSqlParser(FieldDo.class);
        String sql = sp.parse(expr, rc);
        FieldDo[] fields = cc.getBizDao().selectOne(sql, sp.getParams());
        if (fields == null)
            return new SimpleResponseMessage();
        if (fields.length == 1)
            return new SimpleResponseMessage(fields[0].getValue());
        Object[] objs = new Object[fields.length];
        for (int i = 0; i < fields.length; i++)
            objs[i] = fields[i].getValue();
        return new SimpleResponseMessage(objs);
    }

    private IResponseMessage<?> selectMap(CommandContext cc, RecordContext rc,
                                          String expr) {
        SqlParser<Object> sp = RuntimeUtils.createSqlParser(Object.class);
        return new SimpleResponseMessage(cc.getBizDao().selectMapList(sp.parse(expr, rc), null, sp.getParams().toArray()));
    }

    private IResponseMessage<?> selectList(CommandContext cc, RecordContext rc,
                                           String expr) {
        SqlParser<FieldDo> sp = RuntimeUtils.createSqlParser(FieldDo.class);
        String sql = sp.parse(expr, rc);
        List<FieldDo[]> data = cc.getBizDao().selectList(sql, sp.getParams());
        List<Object> result = new ArrayList<Object>(data.size());
        for (FieldDo[] fields : data) {
            if (fields.length == 1)
                result.add(fields[0].getValue());
            else {
                Object[] my = new Object[fields.length];
                for (int i = 0; i < my.length; i++)
                    my[i] = fields[i].getValue();
                result.add(my);
            }
        }
        return new SimpleResponseMessage(result);
    }

    private IResponseMessage<?> executeUpdate(CommandContext cc,
                                              RecordContext rc, String expr) {
        SqlParser<FieldDo> sp = RuntimeUtils.createSqlParser(FieldDo.class);
        String sql = sp.parse(expr, rc);
        cc.getBizDao().update(cc.getUser(), sql, sp.getParams(), false);
        return new SimpleResponseMessage("");
    }
}
